Introduction to R shiny package

Hakan Turgay & Bertan Taylan

Chapter 1

Motivation, Basic R-Shiny Package and Example

Motivation

  • Scientists mostly use R to process their analyzes

  • Presenting/Sharing their findings are usually done in static format

  • Problem : They cannot present additional questions directly

  • Simple idea: Immigrating ratios from a specific region of Turkey due to years and also forecasts about these ratios (e.g. Journalist)

What is Shiny package?

  • Shiny is an R package that makes it easy to build interactive web applications (apps) straight from R. This lesson will get you started building Shiny apps right away.
  • Build useful web applications with only a few lines of code—no JavaScript required.
  • Shiny applications are automatically “live” in the same way that spreadsheets are live. Outputs change instantly as users modify inputs, without requiring a reload of the browser.
  • Shiny user interfaces can be built entirely using R, or can be written directly in HTML, CSS, and JavaScript for more flexibility.
  • Pre-built output widgets for displaying plots, tables, and printed output of R objects.

Basically


R Shiny = R + interactivity + web made easy

kmeans

Install Shiny Package

If you still haven’t installed the Shiny package, open an R session, connect to the internet, and run

install.packages("shiny")

First App in Shiny

library(shiny)
runExample("01_hello")

Code Output

firstapp

More Example

runExample("01_hello")      # a histogram
runExample("02_text")       # tables and data frames
runExample("03_reactivity") # a reactive expression
runExample("04_mpg")        # global variables
runExample("05_sliders")    # slider bars
runExample("06_tabsets")    # tabbed panels
runExample("07_widgets")    # help text and submit buttons
runExample("08_html")       # Shiny app built from HTML
runExample("09_upload")     # file upload wizard
runExample("10_download")   # file download wizard
runExample("11_timer")      # an automated timer

Chapter 2

Structure of a Shiny App

Main Structure

  • ui: Nested R functions that assemble an HTML user interface for the app

  • server : A function with instructions on how to build and rebuild the R objects displayed in the UI

  • shinyApp : Combines ui and server into a functioning app

  • Save the template as app.R

  • a call to the shinyApp function

    library(shiny)
    
    ui <- fluidPage(
     
    )
    
    server <- function(input, output, session) {
      
    }
    
    shinyApp(ui, server)

Alternative Approach

kmeans

Say Hello with Shiny

library(shiny)
ui <- fluidPage(
    "Hello Shiny Package"
    )

server <- function(input, output) {
    
}

shinyApp(ui = ui, server = server)

Code Output

working code:

helloshiny

Chapter 3

Inputs and Outputs

Input and Project Template

Input basit olarak kullanıcı tarafından saglanan herhangi bir degerdir. Bu degeri Rshiny componentleri vasitasiyla onyuzden almayi ve bu bilgilerle server tarafından bir “output” üretmeyi, bu output ile de tekrar onyuz(UI) tarafında birseyleri tetiklemeyi calisacagiz.

  • Rshiny kod şablonlarından asagidakini kullanicaz.
library(shiny)

ui <-  fluidPage()

server <- function(input,output){
  
}

shinyApp(ui = ui, server = server)

Define Input Component

  • Herhangi bir input components oluşturalım .
  • Bu anlatım icin radioButtons components sectik .

library(shiny)

ui <-  fluidPage(
  radioButtons(inputId = "university", label = "Choose an university",
                choices =  c("ITU" = "Istabul Teknik",
                                    "BOUN" = "Bogazici",
                                    "ODTU" = "Ortadogu Teknik")),
  
)

server <- function(input,output){
  
}

shinyApp(ui = ui, server = server)

and More Input Component

Input Types

Input Syntax

Input syntax

Define Output

  • Ana kurgu inputta alinan üniversite bilgisi neticesinde onyuzde belirtilen bir alanda o üniversiteye ait logoyu ekrana basicagiz.
library(shiny)

ui <-  fluidPage(
  radioButtons(inputId = "university", label = "Choose an university",
                choices =  c("ITU" = "Istabul Teknik",
                              "BOUN" = "Bogazici",
                              "ODTU" = "Ortadogu Teknik")),
  imageOutput("universityLogo",height = 300,width = 300),
  
  
)

server <- function(input,output){
  
}

shinyApp(ui = ui, server = server)

and More Output Options

  • Farklı output seçenekleri burdan incelenebiliriz.

Output types

[fig: [https://github.com/rstudio-education/shiny.rstudio.com-tutorial/blob/master/how-to-start-shiny-part-1.pdf]

Define Render on Server Side

  • Input degeri okunup server tarafında render fonksiyonu ile output alanini guncelleyecegiz.
 library(shiny)

ui <-  fluidPage(
  radioButtons(inputId = "university", label = "Choose an university",
                choices =  c("Istabul Technical University" = "ITU",
                             "Bogazici University" = "BOUN",
                             "Middle East Technical University" = "ODTU" )),
  imageOutput("universityLogo",height = 600,width = 600),
  
  
)

server <- function(input,output){
  output$universityLogo <- renderImage({
    if (is.null(input$university))
      return(NULL)
    
    if (input$university == "ITU") {
      return(list(
        src = "./images/university/itu.png",
        contentType = "image/png",
        alt = "ITU"
      ))
    } else if (input$university == "BOUN") {
      return(list(
        src = "./images/university/boun.png",
        filetype = "image/png",
        alt = "Bogazici"
      ))
    } else if (input$university == "ODTU") {
      return(list(
        src = "./images/university/odtu.png",
        filetype = "image/pnd",
        alt = "Ortadogu Teknik"
      ))
    }
    
    
  }, deleteFile = FALSE)
  
}

shinyApp(ui = ui, server = server)

Overview UI

res

Overview Server

res

Code Output

  • Sonuc olarak onyuzden alinan bilgiler bir logoyu secmemize ve server tarafından imageRender fonksiyonu sayesinde onyuzde bu logoyu aktarma işlemini yaptık .
res

Chapter 4

Using Checkbox in Shiny

Reviewing an Example

library(shiny)
runExample("02_text")
kmeans

Starting to Code Analysis

kmeans

Change code

library(shiny)

ui <- fluidPage(
  titlePanel("Shiny Text"),
  sidebarLayout(
    
    sidebarPanel(
      
      checkboxGroupInput("dataset", "Choose a dataset:",
                         c("rock", "pressure", "cars"),
                         selected = "rock"),
                         
      numericInput(inputId = "obs",
                   label = "Number of observations to view:",
                   value = 10)
    ),
    
    mainPanel(
      verbatimTextOutput("summary"),
      tableOutput("view")

Result

working code:

check

Chapter 5

Improving the Design

Design Part

  • Assemble UI with HTML/CSS/… widgets

  • Adjustment of the layout scheme

A Sidebar Component: Sidebar Layout

  • The sidebar layout is a basic model for starting to develop applications.
  • This layout gives a sidebar for inputs and a big area for printing outputs.
  • sidebar
  • Grid Layout

    • This feature is a component of content part.
    • Rows are created by the fluidRow() function and columns defined by the column() function.
    • First parameter of column function is its width (which can take values between 1 and 12).
    • grid

    Tabsets

    • Tabsets are good alternatives in need of subdividing the user interface into discrete sections and are components of content part.
    • tabsetPanel function can be used for creating Tabsets.
    • tabset

    Chapter 6

    Implementations in Shiny: JavaScript, HTML and CSS

    Using HTML in R Shiny


    In R, HTML elements can be defined by tags keyword.
    ui <fluidPage (
    tags$h1(”R Shiny Introduction”) ,
    tags$hr () ,
    tags$br () ,
    tags$p(strong(”Istanbul Technical University”)),
    tags$p(em(”Mathematical Engineering”)),
    tags$a(href=”https://www.itu.edu.tr”,”Website”))
    server <function(input , output){} > 
    shinyApp(ui = ui , server = server)

    tags

    …and more Html Tag

    html tag

    External file import (html, css , js ext)

    Method of importing depends on type of the file;

    To include a CSS file


    use includeCSS() or
    1. Place the file in the www subdirectory
    2. Link to it with:

    tags$head(tags$link(rel = "stylesheet",
    type = "text/css", href = "<file name>"))

    To include JavaScript


    use includeScript() or
    1. Place the file in the www subdirectory
    2. Link to it with:

    tags$head(tags$script(src = "<file name>"))

    To include HTML file

    includeHTML("include.html")

    An Example on Include Javascript

    //myscripts.js file
    document.body.style.backgroundColor = "skyblue";

    javascript

    Result

    kmeans

    Chapter 7

    Introduction to HTMLWidgets Package

    Overview

    • This package builds a framework for creating R bindings to JavaScript libraries. HTMLWidgets can be
      • Used at the R console for data analysis
      • Embedded within R Markdown documents
      • Incorporated into Shiny web applications

    Creating a Widget

    • Three elements forms the widget:

      • Dependencies : JavaScript and CSS assets will be used by the widget
      • R binding : This is the function where R stuff happens
      • JavaScript binding : JavaScript code that connects everything and passes the data and the choices collected from the R binding to the JavaScript library which is (are) using in widget

    Some most-used HTMLWidget Examples

    kmeans


    leaflet

    kmeans


    dygraph

    A Small Case Study: Predicted Deaths From Lung Disease By Using Dygraph Package

    Ui part of the code:

    ui <- fluidPage(
      
      titlePanel("Predicted Deaths from Lung Disease (UK)"),
      
      sidebarLayout(
        sidebarPanel(
          numericInput("months", label = "Months to Predict", 
                       value = 72, min = 12, max = 144, step = 12),
          selectInput("interval", label = "Prediction Interval",
                      choices = c("0.80", "0.90", "0.95", "0.99"),
                      selected = "0.95"),
          checkboxInput("showgrid", label = "Show Grid", value = TRUE),
          hr(),
          div(strong("From: "), textOutput("from", inline = TRUE)),
          div(strong("To: "), textOutput("to", inline = TRUE)),
          div(strong("Date clicked: "), textOutput("clicked", inline = TRUE)),
          div(strong("Nearest point clicked: "), textOutput("point", inline = TRUE)),
          br(),
          helpText("Click and drag to zoom in (double click to zoom back out).")
        ),
        mainPanel(
          dygraphOutput("dygraph")
        )

    Server part of the code

    server <- function(input, output) {
      
      predicted <- reactive({
        hw <- HoltWinters(ldeaths)
        predict(hw, n.ahead = input$months, 
                prediction.interval = TRUE,
                level = as.numeric(input$interval))
      })
      output$dygraph <- renderDygraph({
        dygraph(predicted(), main = "Predicted Deaths/Month") %>%
          dySeries(c("lwr", "fit", "upr"), label = "Deaths") %>%
          dyOptions(drawGrid = input$showgrid)
      })
      output$from <- renderText({
        strftime(req(input$dygraph_date_window[[1]]), "%d %b %Y")      
      })
      output$to <- renderText({
        strftime(req(input$dygraph_date_window[[2]]), "%d %b %Y")
      })
      output$clicked <- renderText({
        strftime(req(input$dygraph_click$x), "%d %b %Y")
      })
      output$point <- renderText({
        paste0('X = ', strftime(req(input$dygraph_click$x_closest_point), "%d %b %Y"), 
               '; Y = ', req(input$dygraph_click$y_closest_point))
      })

    Result

    check

    Creating a widget step-by-step

    Requirements

    • We need to create a new R package that relies on the htmlwidgets package.
    install.packages("htmlwidgets")

    Scaffolding

    To create a new widget you can call the scaffoldWidget function to generate the basic structure for your widget. This function will:

    • Create the .R, .js, and .yaml files required for your widget
    • Tip: If provided, take a Bower package (which is a package manager for web) name and automatically download the JavaScript library (and its dependencies) and add the required entries to the .yaml file. This method is highly preferrable because it guarantees that you get started with the right file structure.

    MyWidget

    We want to create a widget named ‘mywidget’ in a new package of the same name:

    devtools::create("mywidget")               # create package using devtools
    setwd("mywidget")                          # navigate to package dir
    htmlwidgets::scaffoldWidget("mywidget")    # create widget scaffolding
    devtools::install()                        # install the package so we can try it
    • This creates a simple widget that takes a single text argument and displays that text within the widgets HTML element. You can try it like this:
    library(mywidget)
    mywidget("hello, world")
    • This is the most minimal widget possible and doesn’t yet include a JavaScript library.

    Chapter 8

    Publish Project

    Github’ta Barindirma

    • R shiny ile olusturulan projeler dinamik yapida olduklari icin birer R kodu calistiracak makineye ihtiyac duyar. Bu da aylik bir maliyet demektir.
    • Egerki proje bir yerde dursun ve gormek isteyenler localinde tek satir ile calistirsin istiyorsaniz, projeyi githuba atip (App.R veya Server.R kesinlikle icermeli) kullanicinin R konsol ekraninda sirasiyla su kadlari yazmasini saglalisiniz.
    library(shiny)
    runGithub("<proje repo adi>","<proje sahibinin github username>")

    Web site olarak yayimlama

    • Web site olarak yayimlamak icin server ve domaine ihtiyaciniz var. Her ikisi icin ucretli ve ucretsiz(sinirli) kaynaklar bulunmakta. R studio gelistiricilerinin sagladigi 3 secenek sirasiyla .
    • shinyapps.io
    • Shiny Server
    • RStudio Connect

    Projeyi shinyapp.io yayinlama

    • Ortam ucretli bir platformdur fakat 1 uygulamayi ucretsiz olarak host etmenize olanak tanimakta.
    • Bu ortam kullanmasi kolay ve hizlidir ..
    • Yapmaniz gereken https://www.shinyapps.io adresinden kullanici olusturmak.
    • Giris yaptiktan sonra sag ust kosede bulunan token sekmesinden token bilgisini almak.
    • Bu token bilgisini , actigimiz r shiny projesinde console ekraninda sirasiyla yazmak .
    library(rsconnect)
    rsconnect::setAccountInfo(name="<ACCOUNT>", token="<TOKEN>", secret="<SECRET>")
    • Deploy edebilmek icin
    deployApp()

    Rstudiodan shinyapp.io

    • Token basarili bir sekilde verildi ise artik tek tusla yaptiklarimizi paylasabilecegiz.
    check

    Shiny Server ve RStudio Connect

    • Shiny Server : Diyelimki fiziksel bir makine(bilgisayar, vps) kiraladiniz ve burda projemizi besleyen R kodlarini calistiricaz . Bu durumda kiraladigimiz makineye Shiny Server sanal sunucuyu(image) atmamiz gerek.
    • Shiny Server ucretsiz acik kaynak gelistirilen bir projedir.
    • Projenin backend kismi bu sekilde halledilibilinir iken onyuz tarafinda domain(web site adi) satin almaniz gerekecek .
    • RStudio Connect: Yine R studio tarafindan gelistirilen bu ortam ucretli bir servistir. Eger uygulamaniz gelir getirme amacli bir proje ise bu servisi kullanarak bazi hazir servislere erisebilirsiniz .

    [https://shiny.rstudio.com/tutorial/written-tutorial/lesson7/]

    Chapter 8

    Case Studies

    Case Study: Childlessness and Gender Gap By Using Ggraph Package

    • In this section, we will build interactive world maps and show these in the form of an Shiny app.
    • At first, we’re going to start with importing, exploring and cleaning the data that datasets we’re going to use. Since the data sets are dirty, we have to get them in a better shape to make more useful for us.
    • Final forms of our datasets will be like this:

      example

      example

    Creating a function to build map

    • Next, it’s time to define the function that we’ll use for building our world maps. The inputs to this function are the merged data frame, the world data containing geographical coordinates, and the data type, period and indicator the user will select in the R Shiny app.
    worldMaps <- function(df, world_data, data_type, period, indicator){
     
     # Function for setting the aesthetics of the plot
     my_theme <- function () { 
       theme_bw() + theme(axis.text = element_text(size = 14),
                          axis.title = element_text(size = 14),
                          strip.text = element_text(size = 14),
                          panel.grid.major = element_blank(), 
                          panel.grid.minor = element_blank(),
                          panel.background = element_blank(), 
                          legend.position = "bottom",
                          panel.border = element_blank(), 
                          strip.background = element_rect(fill = 'white', colour = 'white'))
     }

    Function Continues:

      # Select only the data that the user has selected to view
      plotdf <- df[df$Indicator == indicator & df$DataType == data_type & df$Period == period,]
      plotdf <- plotdf[!is.na(plotdf$ISO3), ]
      # Add the data the user wants to see to the geographical world data
      world_data['DataType'] <- rep(data_type, nrow(world_data))
      world_data['Period'] <- rep(period, nrow(world_data))
      world_data['Indicator'] <- rep(indicator, nrow(world_data))
      world_data['Value'] <- plotdf$Value[match(world_data$ISO3, plotdf$ISO3)]
      
      # Create caption with the data source to show underneath the map
      capt <- paste0("Source: ", ifelse(data_type == "Childlessness", "United Nations" , "World Bank"))
     # Specify the plot for the world map
      library(RColorBrewer)
      library(ggiraph)
      g <- ggplot() + 
        geom_polygon_interactive(data = world_data, color = 'gray70', size = 0.1,
                                        aes(x = long, y = lat, fill = Value, group = group, 
                                            tooltip = sprintf("%s<br/>%s", ISO3, Value))) + 
        scale_fill_gradientn(colours = brewer.pal(5, "RdBu"), na.value = 'white') + 
        labs(fill = data_type, color = data_type, title = NULL, x = NULL, y = NULL, caption = capt) + 
        my_theme()
      return(g)
    }

    Last Step: Building the Shiny App

    • Now we put our data in a good shape and world mapping function ready and specified.
    • Let’s start with UI.
    # Define the UI
    ui = fluidPage(
      
      # App title
      titlePanel("Childlessness and Gender Gap Index Data"),
      
      # Sidebar layout with input and output definitions
      sidebarLayout(
        
        # Sidebar panel for inputs 
        sidebarPanel(
          
          # First input: Type of data
          selectInput(inputId = "data_type",
                      label = "Choose the type of data you want to see:",
                      choices = list("Childlessness" = "Childlessness", "Gender Gap Index" = "Gender Gap Index")),
          
          # Second input (choices depend on the choice for the first input)
          uiOutput("secondSelection"),
          
          # Third input (choices depend on the choice for the first and second input)
          uiOutput("thirdSelection")
        ),

    Ui Continues with Main Panel:

     # Main panel for displaying outputs
        mainPanel(
          
          # Hide errors
          tags$style(type = "text/css",
                     ".shiny-output-error { visibility: hidden; }",
                     ".shiny-output-error:before { visibility: hidden; }"),
          
          # Output: interactive world map
          girafeOutput("distPlot")
          
        )
      )
    )

    Server side of the app

    # Define the server
    server = function(input, output) {
      
      # Create the interactive world map
      output$distPlot <- renderGirafe({
        ggiraph(code = print(worldMaps(df, world_data, input$data_type, input$period, input$indicator)))
      })
      
      # Change the choices for the second selection on the basis of the input to the first selection
      output$secondSelection <- renderUI({
        choice_second <- as.list(unique(df$Period[which(df$DataType == input$data_type)]))
        selectInput(inputId = "period", choices = choice_second,
                    label = "Choose the period for which you want to see the data:")
      })
      
      # Change the choices for the third selection on the basis of the input to the first and second selections
      output$thirdSelection <- renderUI({
        lab <- ifelse(input$data_type == "Childlessness", "age group", "indicator")
        choice_third <- as.list(unique(df$Indicator[df$DataType == input$data_type & df$Period == input$period]))
        selectInput(inputId = "indicator", choices = choice_third,
                    label = paste0("Choose the type of ", lab, " you want to explore:"))
      })
    }

    Result

    example

    Case Study: Earthquake Map By Using Leaflet Package

    • screenshot

    Distinguishing Depths

    library(shiny)
    library(leaflet)
    library(dplyr)
    library(leaflet.extras)
    
    data <- read.csv("./data/data.csv")
    country <- data["place"]
    
    data$depth_type <-      ## 
        ifelse(
            data$depth <= 70,
            "shallow",
            ifelse(
                data$depth <= 300 | data$depth > 70,
                "intermediate",
                ifelse(data$depth > 300, "deep", "other")

    Forming the UI

    ui <- fluidPage(mainPanel(
        #this will create a space for us to display our map
        leafletOutput(outputId = "mymap"),
        #this allows us to put the checkmarks on top of the map to
        #allow people to view earthquake depth or overlay a heatmap.
        absolutePanel(
            top = 250,
            left = 30,
            checkboxInput("markers", "Depth", FALSE),
            checkboxInput("heat", "Heatmap", FALSE)
        )
    ))

    Server part

    server <- function(input, output, session) {
        #define the color palette for the magnitude of the earthquake
        pal <- colorNumeric(
            palette = c(
                'gold',
                'orange',
                'dark orange',
                'orange red',
                'red',
                'dark red'
            ),
            domain = data$mag
        )
        #define the color of for the depth of the earthquakes
        pal2 <- colorFactor(palette = c('blue', 'yellow', 'red'),
                            domain = data$depth_type)

    Creating the Map

    output$mymap <- renderLeaflet({
    leaflet(data) %>%
      addTiles() %>%
      addFullscreenControl(position = "topleft", pseudoFullscreen = TRUE) %>%
        setView(lng = 35, lat = 42, zoom = 5)  %>% #setting the view over
          addTiles() %>%
          addCircles(
              data = data,
              lat = ~ latitude,
              lng = ~ longitude,
              weight = 0.5,
              radius = ~ sqrt(mag) * 15000,
              popup = ~ as.character(mag),
              label = ~ as.character(paste0("Magnitude: ", sep = " ", mag)),
              color = ~ pal(mag),
              fillOpacity = 0.5

    Making Checkboxes Dynamic With observe Function

    observe({
            proxy <- leafletProxy("mymap", data = data)
            proxy %>% clearMarkers()
            if (input$markers) {
                proxy %>% addCircleMarkers(
                    stroke = FALSE,
                    color = ~ pal2(depth_type),
                    fillOpacity = 0.2,
                    label = ~ as.character(paste0("Magnitude: ", sep = " ", mag))
                ) %>%
                    addLegend(
                        "bottomright",
                        pal = pal2,
                        values = data$depth_type,
                        title = "Depth Type",
                        opacity = 1
                    )
            }
            else {
                proxy %>% clearMarkers() %>% clearControls()
            }

    Map Adjustments with Using leafletproxy Function

    observe({
            proxy <- leafletProxy("mymap", data = data)
            proxy %>% clearMarkers()
            if (input$heat) {
                proxy %>%  addHeatmap(
                    lng =  ~ longitude,
                    lat =  ~ latitude,
                    intensity = ~ mag,
                    blur =  10,
                    max = 0.05,
                    radius = 15
                )
            }
            else{
                proxy %>% clearHeatmap()
            }

    Result

    screenshot

    Case Study: Migration By Using Leaflet Package

    Using Leaflet

    • For creating a map we used Leaflet library.
    • Leaflet is a open source JavaScript library for creating interactive maps.
    • It can be implemented and used in Shiny easily.
    • Leaflet gives the opportunity of adding informational popups to particular part of maps.
    • leaflet

    Result

    result